﻿$Global:Servers=$null
$Global:DeploymentActions=$null
$Global:Configuration=$null


Function Initialize-Environment
{
        [cmdletbinding(DefaultParameterSetName="none")]
        Param( 
        [Parameter(Mandatory=$true,ValueFromPipeline=$false)]
        [ValidateScript({Test-Path $_ -PathType Leaf})] 
        [String] 
        $DeploymentTarget,

        [Parameter(Mandatory=$true,ValueFromPipeline=$false)]
        [ValidateScript({Test-Path $_ -PathType Leaf})] 
        [String] 
        $ActionsList,


        [Parameter(Mandatory=$true,ParameterSetName='environment')]
        [String]
        $Environment
        )

    Begin
    {
        $watch = [system.diagnostics.stopwatch]::StartNew() 
        write-verbose "Start Initialize-Environment from $DeploymentTarget and from $ActionsList"
    }
    End
    {
        write-verbose "End Initialize-Environment from $DeploymentTarget and from $ActionsList"
        $watch.Stop();
        Write-Verbose "Function duration : $($watch.Elapsed)"
    }
    Process 
    {
        if ($Environment)
        {
            $Global:Servers = (Select-Xml -Path $DeploymentTarget -XPath "//server[@environment='$Environment']" | ForEach-Object { New-ObjectServer $_.Node})
        }
        else
        {
            $Global:Servers = (Select-Xml -Path $DeploymentTarget -XPath "//server" | ForEach-Object { New-ObjectServer $_.Node}) 
        }

        $Global:DeploymentActions = (Select-Xml -Path $ActionsList -XPath "//actions" | ForEach-Object { New-ObjectActionLists $_.Node}) 


        #Load the configuration
        $logger = Select-Xml -Path $DeploymentTarget -XPath "//configuration/logger"
        $mailing = Select-Xml -Path $DeploymentTarget -XPath "//configuration/mailing"

        $Global:Configuration = New-Object psobject -Property @{Logger  = $(New-ObjectLoggerConfiguration -source $logger.Node)
                                                                Mailing = $(New-ObjectMailingConfiguration -Source $mailing.Node)
                                                               }  

        #Initialize a default log file if we use this module without a governance process
        $logfile = "DefaultDeploymentLog_$(Get-Date -Format 'yyyyMMdd_HHmm').xml"
        $logfile = [System.IO.Path]::Combine($Configuration.Logger.OutputFolder, $logfile)
        
        $Global:Configuration.Logger.Logfile = $logfile
        Write-Host "Default log file is $($Global:Configuration.Logger.Logfile)" -ForegroundColor Green


    }
}

Function New-ObjectServer
{
        Param( 
        [Parameter(Mandatory=$true,ValueFromPipeline=$true)] 
        [System.Xml.XmlElement] 
        $source
        )

    Begin
    {
        $watch = [system.diagnostics.stopwatch]::StartNew() 
        write-verbose "Start New-ObjectServer from $source"
    }
    End
    {
        write-verbose "End New-ObjectServer from $source"
        $watch.Stop();
        Write-Verbose "New-ObjectServer duration : $($watch.Elapsed)"
    }
    Process 
    {

        #Add mandatory properties
        $server = New-Object System.Object
        $server | Add-Member -type NoteProperty -name Name -value $source.Name
        $server | Add-Member -type NoteProperty -name HostName -value $source.hostName
        $server | Add-Member -type NoteProperty -name Environment -value $source.Environment
        $server | Add-Member -type NoteProperty -name BackupFolder -value $source.BackupFolder
        $server | Add-Member -type NoteProperty -name DeliveryFolder -value $source.DeliveryFolder
        $server | Add-Member -type NoteProperty -name RemoteDeliveryFolder -value $source.remoteDeliveryFolder
        $server | Add-Member -type NoteProperty -name ApplicationManagementModule $source.applicationManagementModule
        $Server | Add-Member -type NoteProperty -name WorkdirFolder $source.workdirFolder
        $Server | Add-Member -type NoteProperty -name WinrmPort $source.winrmPort
        $Server | Add-Member -type NoteProperty -name WinrmUseSSL $source.winrmUseSSL

        #Add Dynamic properties
        $source.Attributes | ForEach-Object{
            
                Write-Verbose "Add dynamic property $($_.Name) from component tag with Value $($_.Value)"
                $Server | Add-Member -type NoteProperty -name $_.Name -value $_.Value -ErrorAction SilentlyContinue
            
        }
        

        $components= @()
        $source.SelectNodes("component") | ForEach-Object {write-verbose $_; $components += New-ObjectComponent $_}

        $server | Add-Member -type NoteProperty -Name Components -Value $components

        Write-Output $server
    }
}

Function New-ObjectComponent
{
 Param( 
        [Parameter(Mandatory=$true,ValueFromPipeline=$true)] 
        [System.Xml.XmlElement] 
        $source
        )

    Begin
    {
        $watch = [system.diagnostics.stopwatch]::StartNew() 
        write-verbose "Start New-ObjectComponent from $source"
    }
    End
    {
        write-verbose "End New-ObjectComponent from $source"
        $watch.Stop();
        Write-Verbose "Function duration : $($watch.Elapsed)"
    }
    Process 
    {
        
        $component = New-Object System.Object
        $component | Add-Member -type NoteProperty -name Name -value $source.Name
        $component | Add-Member -type NoteProperty -name ComponentType -value $source.componentType
        $component | Add-Member -type NoteProperty -name InstallationDirectory -value $source.installationDirectory
        $component | Add-Member -type NoteProperty -name DeploymentSteps -value $(Convert-ToLower -Value $source.deploymentSteps) -Force

        $source.Attributes | ForEach-Object{
            
                Write-Verbose "Add dynamic property $($_.Name) from component tag with Value $($_.Value)"
                $component | Add-Member -type NoteProperty -name $_.Name -value $_.Value -ErrorAction SilentlyContinue
            
        }

        

        Write-Output $component
    }

}

Function Convert-ToLower
{
     Param( 
        [Parameter(Mandatory=$false,ValueFromPipeline=$false)] 
        [String] 
        $Value,

        [Parameter(Mandatory=$false,ValueFromPipeline=$false)] 
        [String] 
        $DefaultValue

        )
    Process
    {
        if (-not $Value)
        {
            if (-not $DefaultValue)
            {
                return [System.String]::Empty
            }
            else
            {
                return $DefaultValue.ToLower()
            }
        }
        else
        {
            return $Value.ToLower()
        }
    }
}



Function New-ObjectComponentAction
{
     Param( 
        [Parameter(Mandatory=$true,ValueFromPipeline=$true)] 
        [System.Xml.XmlElement] 
        $source
        )

    Begin
    {
        $watch = [system.diagnostics.stopwatch]::StartNew() 
        write-verbose "Start New-ObjectComponentAction from $source"
    }
    End
    {
        write-verbose "End New-ObjectComponentAction from $source"
        $watch.Stop();
        Write-Verbose "Function duration : $($watch.Elapsed)"
    }
    Process 
    {
        #add mandatory properties here to execute the ApplicationManagementMode
        $action = New-Object System.Object
        $action | Add-Member -type NoteProperty -name CommandToExecute -value $source.commandToExecute
        $action | Add-Member -type NoteProperty -name Sequence -value $source.sequence
        $action | Add-Member -type NoteProperty -Name SupportWhatif -Value $((Convert-ToLower -Value $source.supportWhatif -DefaultValue 'false') -eq 'true')
        $action | Add-Member -type NoteProperty -Name SupportVerbose -Value $((Convert-ToLower -Value $source.supportVerbose -DefaultValue 'true') -eq 'true')
        $action | Add-Member -type NoteProperty -Name StopOnError -Value $((Convert-ToLower -Value $source.stopOnError -DefaultValue 'false') -eq 'true')



        #retrieve all required properties to run an action with component configuration
        $source.Attributes | ForEach-Object{
            Write-Verbose "Add dynamic property $($_.Name) from component tag with Value $($_.Value)"
            $action | Add-Member -type NoteProperty -name $_.Name -value $_.Value -ErrorAction SilentlyContinue
        }    

        $arguments=@()

        $source.SelectNodes("arguments/argument") | ForEach-Object{

            $arg = New-Object System.Object
            $arg | Add-Member -type NoteProperty -Name Name -Value $_.name
            $arg | Add-Member -type NoteProperty -Name Value -Value $_.value

            $arguments += $arg
           
        }

        $action |Add-Member -type NoteProperty -Name Arguments -Value $arguments

        Write-Output $action
    }

}

Function New-ObjectActionLists
{
    Param( 
        [Parameter(Mandatory=$true,ValueFromPipeline=$true)] 
        [System.Xml.XmlElement] 
        $source
        )

    Begin
    {
        $watch = [system.diagnostics.stopwatch]::StartNew() 
        write-verbose "Start New-ObjectActionLists from $source"
    }
    End
    {
        write-verbose "End New-ObjectActionLists from $source"
        $watch.Stop();
        Write-Verbose "Function duration : $($watch.Elapsed)"
    }
    Process 
    {
        $actionList = New-Object System.Object
        $actionList | Add-Member -type NoteProperty -name ComponentName -value $source.ComponentName
        $actionList | Add-Member -type NoteProperty -name ComponentType -value $source.ComponentType
        $actionList | Add-Member -type NoteProperty -name step -value $source.step

        $actions=@()

        $source.SelectNodes("action") | ForEach-Object{$actions += New-ObjectComponentAction $_}
        $actionList | Add-Member -type NoteProperty -name Actions -value $actions

        Write-Output $actionList
    }
}


Function New-ObjectLoggerConfiguration
{
    Param( 
        [Parameter(Mandatory=$true,ValueFromPipeline=$true)] 
        [System.Xml.XmlElement] 
        $source
        )

    Begin
    {
        $watch = [system.diagnostics.stopwatch]::StartNew() 
        write-verbose "Start New-ObjectLoggerConfiguration from $source"
    }
    End
    {
        write-verbose "End New-ObjectLoggerConfiguration from $source"
        $watch.Stop();
        Write-Verbose "New-ObjectLoggerConfiguration duration : $($watch.Elapsed)"
    }
    Process 
    {
        $loggerConfiguration = New-Object System.Object
        $loggerConfiguration | Add-Member -type NoteProperty -name OutputFolder -value $source.outputFolder
        $loggerConfiguration | Add-Member -type NoteProperty -name ArchivingFolder -value $source.archivingFolder
        $loggerConfiguration | Add-Member -type NoteProperty -name Logfile -value $([System.String]::Empty)
        
        Write-Output $loggerConfiguration
    }
}

Function New-ObjectMailingConfiguration
{
    Param( 
        [Parameter(Mandatory=$true,ValueFromPipeline=$true)] 
        [System.Xml.XmlElement] 
        $source
        )

    Begin
    {
        $watch = [system.diagnostics.stopwatch]::StartNew() 
        write-verbose "Start New-ObjectMailingConfiguration from $source"
    }
    End
    {
        write-verbose "End New-ObjectMailingConfiguration from $source"
        $watch.Stop();
        Write-Verbose "New-ObjectMailingConfiguration duration : $($watch.Elapsed)"
    }
    Process 
    {
        $mailingConfiguration = New-Object System.Object
        $mailingConfiguration | Add-Member -type NoteProperty -name From -value $source.from
        $mailingConfiguration | Add-Member -type NoteProperty -name To -value $source.to
        $mailingConfiguration | Add-Member -type NoteProperty -name Smtpserver -value $source.smtpserver

        #retrieve all required properties to run an action with component configuration
        $source.Attributes | ForEach-Object{
            Write-Verbose "Add dynamic property $($_.Name) from component tag with Value $($_.Value)"
            $mailingConfiguration | Add-Member -type NoteProperty -name $_.Name -value $_.Value -ErrorAction SilentlyContinue
        }    
        
        Write-Output $mailingConfiguration
    }
}

